import logging
import os
import traceback

import numpy as np
import pandas as pd
from collections import Counter
from scipy.stats import norm

from detecter.settings import Collection_frequency

np.random.seed(710)
# 分段时考虑曲线形状和曲线时长两部分，alpha为曲线形状的权重，1- alpha为曲线时长的权重
alpha = 0.5
logger = logging.getLogger('serial.sample')


def get_positive_paths_two_com():
    path = './data/log6.22-6.26/log6.22-6.26'
    file_names_com1, file_names_com2 = [], []
    for file in os.listdir(path):
        file_path = os.path.join(path, file)
        if not os.path.isdir(file_path):
            if file_path.endswith('COM1.csv'):
                file_names_com1.append(file_path)
            if file_path.endswith('COM2.csv'):
                file_names_com2.append(file_path)


# 计算马氏距离的公式
# feat为待测样本,mean是(正样本均值),var为正样本的方差
def cal_madis(feat, mean, var):
    dist = np.sqrt(np.sum(np.square(feat - mean) / (var + 1e-10)))
    return dist  # 取得的是一个值


def cal_gauss_prob(value, mean, var):
    # 扩大方差
    prob = norm.cdf((value - mean) / (np.sqrt(var) * 2))
    return np.abs(1 - (1 - prob) * 2)


# 分段异常率使用 (分段马氏距离 - 样本均值)/方差的方式
# by zxy on 20191227
def cal_seg_times(value, mean, var):
    times = (value - mean) / np.sqrt(var + 1e-10)
    return np.abs(times)


# 获取当前秒
def func_time(t):
    return t.split('.')[0]


def read_csv_file(file_names):
    id_1_or_2 = list()
    for count, csv_path in enumerate(file_names):
        id_1_or_2.append(pd.read_csv(csv_path, header=None))
    id_1_or_2 = pd.concat(id_1_or_2).reset_index(drop=True)
    return id_1_or_2


def get_start_and_end(two_stage):  # 找到所有样本射胶信号开始索引和结束索引
    index = two_stage.index.tolist()
    start_and_end = list()
    is_start = False
    is_end = True
    for i in range(len(index) - 1):
        if is_end:
            is_start = True
            start = i
            is_end = False
        if (index[i + 1] - index[i]) > 2:
            is_end = True
            end = i
        if i + 1 == (len(index) - 1):
            is_end = True
            end = i + 1
        if is_start and is_end:
            start_and_end.append([start, end])
    return start_and_end


def count_records_for_each_second(df):
    # 统计每一秒的记录数以及每一条记录在本秒内对应的索引，并在dataframe中新加两列
    df[0] = df[0].apply(lambda x: func_time(x))  # 对第一列进行apply,使精度为秒
    time_counter = list(Counter(df[0].tolist()).values())
    df[11] = [i for i in time_counter for j in range(i)]
    df[12] = [j + 1 for i in time_counter for j in range(i)]


def get_all_features(two_stage, start_and_end):
    # get all features used to calculate madis from two_stage(id1)
    actual_location = list()
    all_ch2 = list()
    all_speed = list()
    pressure_setting = list()
    all_feat = list()
    len_list = []
    for i in start_and_end:
        len_list.append(i[1] - i[0])
    min_count = np.min(len_list)
    sample_index = np.array(list(range(0, min_count, 8)))
    for i in start_and_end:
        #  4 设定压力,5设定速度,6实际压力,7螺杆位置,8注射速度
        pressure_setting.append(two_stage.iloc[i[0]:i[1] + 1, 3].tolist())
        ch1 = noise_detection(two_stage.iloc[i[0]:i[1] + 1, 5].tolist())
        actual_location.append(ch1)
        ch2 = noise_detection(two_stage.iloc[i[0]:i[1] + 1, 6].tolist())
        all_ch2.append(ch2)
        speed = [0] * len(ch2)
        for j in range(1, len(speed)):
            if j < 10:
                speed[j] = (ch2[j] - ch2[j - 1]) * 80
            else:
                speed[j] = (ch2[j] - ch2[j - 10]) * 8
        all_speed.append(speed)
        ch1_arr = np.array(ch1)
        ch2_arr = np.array(ch2)
        speed_arr = np.array(speed)
        feat = np.concatenate([ch1_arr[sample_index], ch2_arr[sample_index], speed_arr[sample_index]])
        all_feat.append(feat)
    return actual_location, all_ch2, all_speed, pressure_setting, all_feat, min_count


def smoothing_process_v2(action_location, location_index, df):
    value = df.iloc[location_index - 1:location_index, 6]
    ch2_narray = np.array(action_location)
    for i in range(1, len(action_location) - 1):
        if i == 0:
            # ch2_narray[i] = (np.mean(ch2_narray[i:i + 2]) * 2 + value) // 3
            pass
        else:
            ch2_narray[i] = np.mean(ch2_narray[i - 1:i + 2])
    ch2_narray = ch2_narray.astype(np.int)
    return ch2_narray.tolist()


# 平滑, 取五个的平均值
def smoothing_process_v1(action_location):
    ch2_narray = np.array(action_location)
    for i in range(2, len(action_location) - 3):
        ch2_narray[i] = np.mean(ch2_narray[i - 2:i + 3])
    # ch2_narray = ch2_narray.astype(np.int)  # 这里做了规整，
    return ch2_narray.tolist()


# 噪声检测
def noise_detection(action_location):
    for i in range(1, len(action_location) - 1):
        if np.abs(action_location[i] - action_location[i - 1]) > 250 and np.abs(action_location[i + 1] - action_location[i]) > 250:
            # print('Noise Point Detected!')
            action_location[i] = (action_location[i + 1] + action_location[i - 1]) // 2
    return action_location


def get_every_seg_for_positive_portrait_v2(N, sample_index, point1, point2, portrait_pressure, portrait_location,
                                           portrait_speed,
                                           pressure_var, location_var, speed_var):
    """
    得到各段内的正样本画像时，同样做了重采样，计算各段之间的马氏距离时更精确
    """
    if point2 == N:
        len_seg = len(sample_index[(point1 <= sample_index) & (sample_index <= point2)])  # 得到后面一个分段区间
    else:
        len_seg = len(sample_index[(point1 <= sample_index) & (sample_index < point2)])  ## 得到sample_index中大于等于point1,小于等于point2的值，并判断其长度。实际上是得到前面一个分段区间
    #     开始点,结束点, 元素个数
    sample_index_seg = np.linspace(point1, point2, len_seg, endpoint=False, dtype=np.int32)  # 在point1和point2之间生成len_seg个值
    portrait_feat_seg = np.concatenate(
        [portrait_pressure[sample_index_seg], portrait_location[sample_index_seg], portrait_speed[sample_index_seg]])
    feat_var_seg = np.concatenate(
        [pressure_var[sample_index_seg], location_var[sample_index_seg], speed_var[sample_index_seg]])
    return sample_index_seg, len_seg, portrait_feat_seg, feat_var_seg


def cal_segmentation_time(seg_points, portrait_seg_points):
    td_list = []
    # 对特定样本分段
    # 最后一点
    for i in range(len(seg_points) - 1):
        td = (seg_points[i + 1] - seg_points[i] - portrait_seg_points[i + 1] + portrait_seg_points[i]) / 80
        td_list.append(td)
    # 返回每段时间差距, 以及分段点,和段长度
    return td_list


def cal_segmentation_madis(id1_ch1, id1_ch2, id1_speed, seg_points, len_seg_list, portrait_feat_seg_list,
                           feat_var_seg_list):
    # len_seg1 正样本画像在该段取的点数
    dist_segs = []
    for i in range(len(seg_points) - 1):  # seg_points是一个一维的射胶保压分段列表，长度为3
        sample_index = np.linspace(seg_points[i], seg_points[i + 1], len_seg_list[i], endpoint=False, dtype=np.int32)
        feature = np.concatenate(
            [np.array(id1_ch1)[sample_index], np.array(id1_ch2)[sample_index], np.array(id1_speed)[sample_index]])
        if isinstance(portrait_feat_seg_list[i], list):
            portrait_feat_seg_list[i] = np.array(portrait_feat_seg_list[i])
            feat_var_seg_list[i] = np.array(feat_var_seg_list[i])
        dist_seg = cal_madis(feature, portrait_feat_seg_list[i], feat_var_seg_list[i])
        dist_segs.append(dist_seg)
    return dist_segs


def cal_segmentation_madis_and_time(dist_segs_means, dist_segs_vars, td_segs_means, td_segs_vars, dist_segs, td_segs):
    prob_segs_result = []
    for i in range(len(dist_segs)):
        prob_seg_dist = cal_gauss_prob(dist_segs[i], dist_segs_means[i], dist_segs_vars[i])
        prob_seg_td = cal_gauss_prob(td_segs[i], td_segs_means[i], td_segs_vars[i])
        prob_seg_total = alpha * prob_seg_dist + (1 - alpha) * prob_seg_td
        prob_segs_result.append([prob_seg_dist, prob_seg_td, prob_seg_total])
    return prob_segs_result


def cal_madis_whole_curve(N, sample_ch1, sample_ch2, sample_speed, portrait_feat, feat_var):
    try:
        sample_ch1 = np.array(sample_ch1)
        sample_ch2 = np.array(sample_ch2)
        sample_speed = np.array(sample_speed)
        portrait_feat = np.array(portrait_feat)
        feat_var = np.array(feat_var)
        sample_index = np.array(list(range(0, N, Collection_frequency)))
        all_feat = np.concatenate([sample_ch1[sample_index], sample_ch2[sample_index], sample_speed[sample_index]])
        dist = cal_madis(all_feat, portrait_feat, feat_var)
        return dist
    except Exception as e:
        logger.error(e)
        logger.error(traceback.print_exc())


# 样本总体马氏距离的均值与方差
def cal_madis_list(all_feat, portrait_feat, feat_var):
    mahalanobis_distance = list()
    for i in range(len(all_feat)):  # 对每一条数据计算madis
        dist = cal_madis(all_feat[i], portrait_feat, feat_var)
        mahalanobis_distance.append(dist)
    dist_mean = np.mean(mahalanobis_distance)
    dist_var = np.var(mahalanobis_distance)
    return dist_mean, dist_var  # 返回两个值


def get_speed_by_location(location_list):
    speed = [0] * len(location_list)  # 实际速度
    for j in range(1, len(speed)):
        if j < 10:
            speed[j] = (location_list[j] - location_list[j - 1]) * 80
        else:
            speed[j] = (location_list[j] - location_list[j - 10]) * 8
    return speed


def cal_total_madis_times(total_madis, feat_dis, dis_var):
    times = (total_madis - feat_dis) / np.sqrt(dis_var)
    return np.abs(times)


def reduce_portrait_length(figure, length):
    portrait_feat = np.array(figure.feature)
    feat_var = np.array(figure.feature_var)
    if length < figure.length:
        actual_pressure = figure.actual_pressure[:length]
        actual_location = figure.actual_location[:length]
        actual_speed = figure.actual_speed[:length]
        pressure_var = figure.pressure_var[:length]
        location_var = figure.location_var[:length]
        speed_var = figure.speed_var[:length]
        sample_index = np.array(list(range(0, length, 8))).astype('int32')
        portrait_feat = np.concatenate(
            [actual_pressure[sample_index], actual_location[sample_index],
             actual_speed[sample_index]])
        feat_var = np.concatenate([pressure_var[sample_index], location_var[sample_index], speed_var[sample_index]])
    return portrait_feat, feat_var
